// -*- C++ -*-
#ifndef SDATASET_H
#define SDATASET_H

#include <vector>
#include <string>
#include <cassert>

#include "sattribute.h"
#include "sdataview.h"

namespace sds {


  class SDataset {
    friend class SFile;

  public:

    typedef std::vector<SInteger> MultiIndex;

    ~SDataset();

    const std::string& getName() const { return name; }

    MultiIndex getSize() const { return size; }

    SDataType getDataType() const { return dataType; }

    SInteger getAttributeCount() const { return attributes; }

    SAttribute getAttribute(int index) const { 
      return SAttribute(datasetId, index);
    }

    SAttribute getAttribute(const std::string& name) const;

    template <typename Iterator>
    void readData(Iterator it, MultiIndex start, 
		  MultiIndex size, MultiIndex stride) const;

    template <typename Iterator>
    void readData(Iterator it, MultiIndex start, MultiIndex size) const;

    template <typename Iterator>
    void readData(Iterator it) const;


    template <typename Type>
    SDataView<Type> getDataView(MultiIndex start, MultiIndex size,
				MultiIndex stride) const;

    template <typename Type>
    SDataView<Type> getDataView(MultiIndex start, MultiIndex size) const;

    template <typename Type>
    SDataView<Type> getDataView() const;



    template <typename Iterator>
    void writeData(Iterator it, MultiIndex start, 
		  MultiIndex size, MultiIndex stride);

    template <typename Iterator>
    void writeData(Iterator it, MultiIndex start, MultiIndex size);

    template <typename Iterator>
    void writeData(Iterator it);


    template <typename Type>
    void setDataView(const SDataView<Type>& dataview, MultiIndex start, 
		     MultiIndex size, MultiIndex stride);

    template <typename Type>
    void setDataView(const SDataView<Type>& dataview, MultiIndex start, 
		     MultiIndex size);

    template <typename Type>
    void setDataView(const SDataView<Type>& dataview);

  private:
    SDataset(int fileId, int index);

    struct Reader {
    public:
      Reader(const SDataset& _dataset, MultiIndex _start, 
	     MultiIndex _size, MultiIndex _stride) 
	: dataset(_dataset), start(_start), size(_size), stride(_stride) {}

      SDataType getDataType() const { return dataset.getDataType(); }

      int getLength() const;
      void read(void* buffer) const;
      
    private:
      const SDataset& dataset;

      MultiIndex start;
      MultiIndex size;
      MultiIndex stride;
    };

    struct Writer {
    public:
      Writer(const SDataset& _dataset, MultiIndex _start, 
	     MultiIndex _size, MultiIndex _stride) 
	: dataset(_dataset), start(_start), size(_size), stride(_stride) {}

      SDataType getDataType() const { return dataset.getDataType(); }

      int getLength() const;
      void write(void* buffer) const;
      
    private:
      const SDataset& dataset;

      MultiIndex start;
      MultiIndex size;
      MultiIndex stride;
    };

    SInteger datasetId;
  
    std::string name;
    MultiIndex size;

    SDataType dataType;
    SInteger attributes;
  };



  template <typename Iterator>
  void SDataset::readData(Iterator it, MultiIndex start, 
			  MultiIndex size, MultiIndex stride) const {
    _SDecode<SDataTypeList>().read(it, Reader(*this, start, size, stride));
  }

  template <typename Iterator>
  void SDataset::readData(Iterator it, MultiIndex start, 
			  MultiIndex size) const {
    MultiIndex stride(getSize().size(), 1);
    _SDecode<SDataTypeList>().read(it, Reader(*this, start, size, stride));
  }

  template <typename Iterator>
  void SDataset::readData(Iterator it) const {
    MultiIndex stride(getSize().size(), 1);
    MultiIndex start(getSize().size(), 0);
    _SDecode<SDataTypeList>().read(it, Reader(*this, start, size, stride));
  }


  template <typename Type> SDataView<Type> 
  SDataset::getDataView(MultiIndex start, MultiIndex size,
			MultiIndex stride) const {
    SDataView<Type> dataView(size);
    readData(dataView.data().begin(), start, size, stride);
    return dataView;
  }

  template <typename Type> SDataView<Type> 
  SDataset::getDataView(MultiIndex start, MultiIndex size) const {
    SDataView<Type> dataView(size);
    readData(dataView.data().begin(), start, size);
    return dataView;
  }

  template <typename Type>
  SDataView<Type> SDataset::getDataView() const {
    SDataView<Type> dataView(size);
    readData(dataView.data().begin());
    return dataView;
  }

  template <typename Iterator>
  void SDataset::writeData(Iterator it, MultiIndex start, 
			   MultiIndex size, MultiIndex stride) {
    _SEncode<SDataTypeList>().write(it, Writer(*this, start, size, stride));
  }

  template <typename Iterator>
  void SDataset::writeData(Iterator it, MultiIndex start, MultiIndex size) {
    MultiIndex stride(getSize().size(), 1);
    _SEncode<SDataTypeList>().write(it, Writer(*this, start, size, stride));
  }

  template <typename Iterator>
  void SDataset::writeData(Iterator it) {
    MultiIndex stride(getSize().size(), 1);
    MultiIndex start(getSize().size(), 0);
    _SEncode<SDataTypeList>().write(it, Writer(*this, start, size, stride));
  }

  template <typename Type> void 
  SDataset::setDataView(const SDataView<Type>& dataview, MultiIndex start, 
			MultiIndex size, MultiIndex stride) {
    writeData(dataview.begin(), start, size, stride);
  }

  template <typename Type> void 
  SDataset::setDataView(const SDataView<Type>& dataview, MultiIndex start, 
			MultiIndex size) {
    writeData(dataview.begin(), start, size);
  }

  template <typename Type> void 
  SDataset::setDataView(const SDataView<Type>& dataview) {
    writeData(dataview.data().begin());
  }

}
#endif
